﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using StyleMVVM.DependencyInjection;
using StyleMVVM.DependencyInjection.Impl;

namespace StyleMVVM.DependencyInjection
{
	/// <summary>
	/// C# extensions for IDependencyInjectionContainer
	/// </summary>
	public static class IDependencyInjectionContainerExtension
	{
		/// <summary>
		/// Removes all the extra exports that are releated to XAML based development
		/// </summary>
		/// <param name="container"></param>
		public static void RemoveXAMLExports(this IDependencyInjectionContainer container)
		{
			container.BlackListExportType(typeof(StyleMVVM.Suspension.FrameSuspensionManager));
			container.BlackListExportType(typeof(StyleMVVM.ViewModel.Impl.ViewModelNavigationBinder));
			container.BlackListExportType(typeof(StyleMVVM.ViewModel.Impl.ViewModelDataContextBinder));
			container.BlackListExportType(typeof(StyleMVVM.ViewModel.Impl.ViewModelParentDataContextBinder));
			container.BlackListExportType(typeof(StyleMVVM.ViewModel.Impl.ViewModelViewAwareBinder));
			container.BlackListExportType(typeof(StyleMVVM.ViewModel.Impl.ViewModelLoadedBinder));
			container.BlackListExportType(typeof(StyleMVVM.ViewModel.BaseViewModel));
			container.BlackListExportType(typeof(StyleMVVM.View.Impl.FauxNavigationService));
			container.BlackListExportType(typeof(StyleMVVM.ViewModel.RegionAwareViewModel));
			container.BlackListExportType(typeof(StyleMVVM.ViewModel.PageViewModel));
			container.BlackListExportType(typeof(StyleMVVM.View.Impl.ViewBinderService));
			container.BlackListExportType(typeof(StyleMVVM.View.Impl.NavigationService));
			container.BlackListExportType(typeof(StyleMVVM.View.Regions.Adapters.PanelAdapter));
			container.BlackListExportType(typeof(StyleMVVM.View.Regions.Adapters.ContentPresenterAdapter));
			container.BlackListExportType(typeof(StyleMVVM.View.Regions.Adapters.ItemsControlAdapter));
			container.BlackListExportType(typeof(StyleMVVM.View.Regions.Adapters.ContentControlAdapter));
			container.BlackListExportType(typeof(StyleMVVM.View.Regions.Adapters.FrameAdapter));
			container.BlackListExportType(typeof(StyleMVVM.View.Impl.FauxFilePickerService));
			container.BlackListExportType(typeof(StyleMVVM.View.Impl.FilePickerService));
			container.BlackListExportType(typeof(StyleMVVM.ViewModel.NavigatingViewModel));
			container.BlackListExportType(typeof(StyleMVVM.View.Impl.UIVisualizationService));
			container.BlackListExportType(typeof(StyleMVVM.ViewModel.Impl.ViewModelResolutionService));
			container.BlackListExportType(typeof(StyleMVVM.View.Impl.CharmService));
			container.BlackListExportType(typeof(StyleMVVM.View.Regions.RegionManager));
			container.BlackListExportType(typeof(StyleMVVM.View.Impl.MessageBoxService));
			container.BlackListExportType(typeof(StyleMVVM.ViewModel.PopupViewModel));
			container.BlackListExportType(typeof(StyleMVVM.ViewModel.OverlayViewModel));

#if DOT_NET

			container.BlackListExportType(typeof(StyleMVVM.ViewModel.Impl.ViewModelWindowBinder));
			container.BlackListExportType(typeof(StyleMVVM.ViewModel.WindowViewModel));
			container.BlackListExportType(typeof(StyleMVVM.View.MessageBoxWindowViewModel));

#endif
		}

		/// <summary>
		/// Locate an export based upon type
		/// </summary>
		/// <typeparam name="T">type to locate</typeparam>
		/// <param name="container">container to locate from</param>
		/// <param name="parameters">parameters to pass upon construction</param>
		/// <returns>located type</returns>
		public static T Locate<T>(this IDependencyInjectionContainer container,
		                          params object[] parameters)
		{
			return (T)container.LocateByType(typeof(T), parameters);
		}

		/// <summary>
		/// Locate a particular export while specifying the injection context
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="container"></param>
		/// <param name="injectionContext"></param>
		/// <param name="parameters"></param>
		/// <returns></returns>
		public static T LocateWithContext<T>(this IDependencyInjectionContainer container,
		                                     IInjectionContext injectionContext,
		                                     params object[] parameters)
		{
			return (T)container.LocateByTypeWithContext(injectionContext, typeof(T), parameters);
		}

		/// <summary>
		/// Locate all exports of a particular type
		/// </summary>
		/// <typeparam name="T">type to locate</typeparam>
		/// <param name="container">container to locate from</param>
		/// <returns>located types</returns>
		public static IEnumerable<T> LocateAll<T>(this IDependencyInjectionContainer container)
		{
			return container.LocateAllByType(typeof(T)).OfType<T>().ToList();
		}

		public static IEnumerable<T> LocateAllWithContext<T>(this IDependencyInjectionContainer container,
		                                                     IInjectionContext injectionContext)
		{
			return container.LocateAllByTypeWithContext(injectionContext, typeof(T)).OfType<T>().ToList();
		} 

		/// <summary>
		/// Registers a type for export
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="container"></param>
		/// <returns></returns>
		public static IExportConfiguration<T> Register<T>(this IDependencyInjectionContainer container)
		{
			IContainerBaseExportConfigurationAware aware = container as IContainerBaseExportConfigurationAware;

			if (aware != null)
			{
				IExportConfiguration<T> returnValue =
					new ExportConfiguration<T>(new InstanceExportStrategy(typeof(T)));

				aware.AddExportConfiguration(returnValue);

				return returnValue;
			}

			throw new ArgumentNullException("container", "container must implement IContainerBaseExportConfigurationAware");
		}

		/// <summary>
		/// Register a new Function for export in the DI container. Where T is 
		/// </summary>
		/// <typeparam name="T">type the function returns</typeparam>
		/// <param name="container">container</param>
		/// <param name="exportFunction">export function</param>
		/// <returns></returns>
		public static IExportConfiguration<T> RegisterExportFunction<T>(this IDependencyInjectionContainer container,
		                                                                Func
			                                                                <IDependencyInjectionContainer, IInjectionContext,
			                                                                object[], T>
			                                                                exportFunction)
		{
			IContainerBaseExportConfigurationAware aware = container as IContainerBaseExportConfigurationAware;

			if (aware != null)
			{
				IExportConfiguration<T> returnValue =
					new ExportConfiguration<T>(new FunctionExportStrategy(typeof(T), (x, y, z) => exportFunction(x, y, z)));

				aware.AddExportConfiguration(returnValue);

				return returnValue;
			}

			throw new ArgumentNullException("container", "container must implement IContainerBaseExportConfigurationAware");
		}

		/// <summary>
		/// Registers an actual Func(T) for export (i.e. you import a Func(T) delegate not T)
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="container"></param>
		/// <param name="func"></param>
		/// <returns></returns>
		public static IExportConfiguration<Func<T>> RegisterFuncForExport<T>(this IDependencyInjectionContainer container,
																							Func<T> func)
		{
			return container.RegisterExportFunction((x, y, z) => func).As<Func<T>>();
		}
	}
}